package com.shhic.hhplt.server.biz;

import com.shhic.hhplt.server.model.ExportedTestReport;
import com.shhic.hhplt.server.model.TestReportAutoExportConfig;
import com.shhic.hhplt.server.model.TestReportExportResult;
import com.shhic.hhplt.server.service.ITestComplexQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 自动导出测试数据的工具
 * @author zws
 *
 */
@Component
@ConfigurationProperties(prefix = "upload")
@Slf4j
public class TestReportAutoExportUtil implements ITestReportAutoExportUtil {
	
	@Autowired
	ITestComplexQueryService tcxqService;
	@Autowired
	IFtpHelper ftpHelper;
	private Timer timer;	//定时器
	private String currentSaveFolder;	//当前要保存的文件夹（路径）

	private Map<String,String> props=new HashMap<>(); //ftp上传文件相关属性

	public Map<String, String> getProps() {
		return props;
	}

	public void setProps(Map<String, String> props) {
		this.props = props;
	}
	/**
	 * 开始（如果有）自动导出的定时器线程
	 */
	@PostConstruct
	public void checkAndStartAutoExportThread(){
		props.put("isExporting","false");
		props.put("isUploading","false");
		boolean autoStart = Boolean.parseBoolean(props.get("startAutoExport"));
		if(autoStart){
			long interval = Long.parseLong(props.get("interval")) * 3600 * 1000;	//从小时换算成毫秒
//			long interval = Long.valueOf(prop.getProperty("autoExport.interval")) * 1000;	//打桩，单位是秒
			log.info("启动自动导出测试数据定时任务，间隔："+interval);
			timer = new Timer();
			timer.scheduleAtFixedRate(new AutoRecordTimerTask(),getNextExecuteTime(),interval);
		}
	}
	
	/**
	 * 下次开始时间
	 * @return
	 */
	private Date getNextExecuteTime(){
		long lastExecuteTime = Long.parseLong(props.get("lastExportTimestamp"));
		if(lastExecuteTime != 0){
			long interval = Long.parseLong(props.get("interval")) * 3600 * 1000;	//从小时换算成毫秒
//			long interval = Long.valueOf(prop.getProperty("autoExport.interval")) * 1000;	//打桩，单位是秒
			long now = new Date().getTime();
			now = lastExecuteTime + ((now - lastExecuteTime)/interval + 1 ) * interval;
			return new Date(now);
		}else{
			return new Date();
		}
	}
	
	
	/**
	 * 导出逻辑-定时器任务
	 * @author user
	 *
	 */
	private class AutoRecordTimerTask extends TimerTask {
		@Override
		public void run() {
			synchronized(ftpHelper){
				props.put("isExporting", "true");
				Date lastExecuteTime = new Date(Long.parseLong(props.get("lastExportTimestamp")));
				Date now = new Date();
				log.info("开始导出数据"+lastExecuteTime+"~"+now);
				
				long total = 0,exported = 0;
				try{
					if(!createNewExportForder(lastExecuteTime,now)){
						log.error("创建文件夹["+currentSaveFolder+"]失败，数据导出终止");
						props.put("isExporting", "false");
						return;
					}
					//一天一天的数据吧，一下子内存会漏的
					Calendar c = Calendar.getInstance();
					c.setTime(lastExecuteTime);
					while(c.getTime().before(now)){
						Date startTime = c.getTime();
						c.add(Calendar.HOUR, 2);
						Date endTime = c.getTime().before(now)? c.getTime():now;
						int[] res = loadAndExportReport(startTime,endTime);
						total += res[0];
						exported += res[1];
					}
					log.info("导出完成，拟导出:"+total+",实际导出:"+exported);
					props.put("isExporting", "false");
				}catch(Exception e){
					log.error(null,e);
				}finally{
					props.put("isExporting", "false");
				}
				if(total==0 || ((float)exported/total)>0.9){
					props.put("lastExportTimestamp", Long.toString(now.getTime()));
					if(/*exported!=0 &&*/ Boolean.parseBoolean(props.get("uploadToFtp")))uploadToFtp(currentSaveFolder);
				}else{
					log.error("导出数量太少，认定为失败，等下个周期再次尝试");
				}
			}
		}
	}
	
	/**
	 * 上传到ftp
	 */
	@Override
	public void uploadToFtp(String dirName){
		final String ftpUrl = props.get("url");
		final String username = props.get("username");
		final String password = props.get("password");
		props.put("isUploading", "true");
		
		if(!dirName.contains("/")){
			dirName = props.get("savePath")+"/"+dirName;	//如果是相对路径，就换算成绝对路径
		}
		final String finalDirName = dirName;
		new Thread(){
			public void run() {
				log.info("准备上传导出的测试报告文件...");
				synchronized(ftpHelper){
					try{
						ftpHelper.openFtp(ftpUrl,username,password);
						ftpHelper.uploadFolder(finalDirName);
						//上传完成后改名
						if(!finalDirName.contains("(已上传ftp)")){
							File f = new File(finalDirName);
							f.renameTo(new File(finalDirName+"(已上传ftp)"));
						}
						log.info("测试报告上传ftp完成");
			
					}catch(Exception e){
						log.error("测试报告上传失败",e);
					}finally{
						props.put("isUploading", "false");
						ftpHelper.close();
					}
				}
			};
		}.start();
		
	}
	
	/**
	 * 创建用于保存输出数据的文件夹
	 * @param lastExecuteTime
	 * @param now
	 * @return
	 */
	private boolean createNewExportForder(Date lastExecuteTime,Date now){
		currentSaveFolder = String.format("%s/%s-%s",
				props.get("savePath"),
				new SimpleDateFormat("yyyy年MM月dd日HH时mm分").format(lastExecuteTime),
				new SimpleDateFormat("MM月dd日HH时mm分").format(now)
				);
		return new File(currentSaveFolder).mkdir();
	}
	
	/**
	 * 导出并保存报告
	 * @param startTime	开始时间
	 * @param endTime	结束时间
	 * @return	[应导出数,实际导出数]
	 */
	private int[] loadAndExportReport(Date startTime,Date endTime){
		List<ExportedTestReport> reports = tcxqService.exportTestReports(startTime.getTime(),endTime.getTime());
		int total = reports.size(),exported=0;
		for(ExportedTestReport report : reports){
			if(saveReportToXmlFile(report))exported ++;
		}
		reports.clear();
		System.gc();	//由于处理大数据，来不及自动GC，因此手动调用，以防止内存Overflow
		log.info("已导出"+startTime+"至"+endTime+"的"+exported+"条记录");
		
		return new int[]{total,exported};
	}
	
	
	/**
	 * 保存报告到XML文件
	 * 
	 * @param report	报告对象
	 * @return 	导出成功返回true，否则返回false
	 */
	public boolean saveReportToXmlFile(ExportedTestReport report ) {
		File reportFile = new File(currentSaveFolder+"/"+report.getReportName().replace("\\", "_")+".xml");
		if(reportFile.exists()){
			reportFile.delete();
		}
		try {
			FileWriter fw = new FileWriter(reportFile);
			fw.write(report.getReport());
			fw.close();
			return true;
		} catch (IOException e) {
			log.error(null,e);
			return false;
		}
	}
	
	
	@Override
	public TestReportAutoExportConfig loadConfig() {
		TestReportAutoExportConfig t = new TestReportAutoExportConfig();
		t.setIntervalInHour(Integer.parseInt(props.get("interval")));
		t.setLastExportTimestamp(Long.parseLong(props.get("lastExportTimestamp")));
		t.setSavePath(props.get("savePath"));
		t.setStartAutoExport(Boolean.parseBoolean(props.get("startAutoExport")));
		t.setExporting(Boolean.parseBoolean(props.get("isExporting")));
		t.setUploading(Boolean.parseBoolean(props.get("isUploading")));

		t.setUploadToFtp(Boolean.parseBoolean(props.get("uploadToFtp")));
		t.setUploadFtpUrl(props.get("url"));
		t.setUploadFtpUser(props.get("username"));
		t.setUploadFtpPassword(props.get("password"));
		return t;
	}

	@Override
	public void saveConfig(TestReportAutoExportConfig cfg) {
		boolean oldStartAutoExport =  Boolean.parseBoolean(props.get("startAutoExport"));
		props.put("startAutoExport", Boolean.toString(cfg.isStartAutoExport()));
		props.put("savePath", cfg.getSavePath());
		props.put("interval", Integer.toString(cfg.getIntervalInHour()));
		props.put("lastExportTimestamp", Long.toString(cfg.getLastExportTimestamp()));
		props.put("uploadToFtp",Boolean.toString(cfg.isUploadToFtp()));
		props.put("url",cfg.getUploadFtpUrl());
		props.put("username",cfg.getUploadFtpUser());
		props.put("password",cfg.getUploadFtpPassword());
		
		if(oldStartAutoExport){
			timer.cancel();
			timer = null;
		}
		checkAndStartAutoExportThread();
	}
	
	@Override
	public void startExportImmediately() {
		boolean autoStart = Boolean.parseBoolean(props.get("startAutoExport"));
		if(autoStart){
			timer.cancel();
			timer = null;
		}
		new Thread(){
			public void run() {
				new AutoRecordTimerTask().run();
				checkAndStartAutoExportThread();
			};
		}.start();
	}
	
	@Override
	public List<TestReportExportResult> getTestReportExportResultList() {
		List<TestReportExportResult> lt = new LinkedList<TestReportExportResult>();
		File baseDir = new File(props.get("savePath"));
		File[] listFiles = baseDir.listFiles();
		
		if(listFiles != null){
			int folderCount = listFiles.length;
			int startCount = folderCount>10 ? folderCount-10:0;	//最多显示10条(倒着排)
			for(int i=folderCount-1;i>=startCount;i--){
				File f = listFiles[i];
				if(!f.isDirectory()){
					continue;
				}
				TestReportExportResult e = new TestReportExportResult();
				e.setCount(f.list().length);
				e.setForderName(f.getName());
				e.setPath(f.getPath());
				lt.add(e);
			}
		}
		return lt;
	}
	
	
	@Override
	public boolean deleteDir(String dirName) {
		if(!dirName.contains("/")){
			dirName = props.get("savePath")+"/"+dirName;	//如果是相对路径，就换算成绝对路径
		}
		synchronized(ftpHelper){	//如果正在上传中，不能删除
			File dirFile = new File(dirName);
			File[] files = dirFile.listFiles();
			if(files != null){
				for(File f : files){
					if(!f.delete())return false;
				}
			}
			return dirFile.delete();
		}
	}
}
